<?php
/**
 * Created by 784855684@qq.com.
 * Link: https://github.com/lichtung/wshore
 * User: linzh
 * Date: 6/25/17
 * Time: 6:12 PM
 */
declare(strict_types=1);

namespace {

/// pack 64-bit signed
    function sphPackI64($v)
    {
        assert(is_numeric($v));
        // x64
        if (PHP_INT_SIZE >= 8) {
            $v = (int)$v;
            return pack("NN", $v >> 32, $v & 0xFFFFFFFF);
        }
        // x32, int
        if (is_int($v))
            return pack("NN", $v < 0 ? -1 : 0, $v);
        // x32, bcmath
        if (function_exists("bcmul")) {
            if (bccomp($v, 0) == -1)
                $v = bcadd("18446744073709551616", $v);
            $h = bcdiv($v, "4294967296", 0);
            $l = bcmod($v, "4294967296");
            return pack("NN", (float)$h, (float)$l); // conversion to float is intentional; int would lose 31st bit
        }
        // x32, no-bcmath
        $p = max(0, strlen($v) - 13);
        $lo = abs((float)substr($v, $p));
        $hi = abs((float)substr($v, 0, $p));
        $m = $lo + $hi * 1316134912.0; // (10 ^ 13) % (1 << 32) = 1316134912
        $q = floor($m / 4294967296.0);
        $l = $m - ($q * 4294967296.0);
        $h = $hi * 2328.0 + $q; // (10 ^ 13) / (1 << 32) = 2328
        if ($v < 0) {
            if ($l == 0)
                $h = 4294967296.0 - $h;
            else {
                $h = 4294967295.0 - $h;
                $l = 4294967296.0 - $l;
            }
        }
        return pack("NN", $h, $l);
    }

/// pack 64-bit unsigned
    function sphPackU64($v)
    {
        assert(is_numeric($v));
        // x64
        if (PHP_INT_SIZE >= 8) {
            assert($v >= 0);
            // x64, int
            if (is_int($v))
                return pack("NN", $v >> 32, $v & 0xFFFFFFFF);
            // x64, bcmath
            if (function_exists("bcmul")) {
                $h = bcdiv($v, 4294967296, 0);
                $l = bcmod($v, 4294967296);
                return pack("NN", $h, $l);
            }
            // x64, no-bcmath
            $p = max(0, strlen($v) - 13);
            $lo = (int)substr($v, $p);
            $hi = (int)substr($v, 0, $p);
            $m = $lo + $hi * 1316134912;
            $l = $m % 4294967296;
            $h = $hi * 2328 + (int)($m / 4294967296);
            return pack("NN", $h, $l);
        }
        // x32, int
        if (is_int($v))
            return pack("NN", 0, $v);
        // x32, bcmath
        if (function_exists("bcmul")) {
            $h = bcdiv($v, "4294967296", 0);
            $l = bcmod($v, "4294967296");
            return pack("NN", (float)$h, (float)$l); // conversion to float is intentional; int would lose 31st bit
        }
        // x32, no-bcmath
        $p = max(0, strlen($v) - 13);
        $lo = (float)substr($v, $p);
        $hi = (float)substr($v, 0, $p);
        $m = $lo + $hi * 1316134912.0;
        $q = floor($m / 4294967296.0);
        $l = $m - ($q * 4294967296.0);
        $h = $hi * 2328.0 + $q;
        return pack("NN", $h, $l);
    }

// unpack 64-bit unsigned
    function sphUnpackU64($v)
    {
        list ($hi, $lo) = array_values(unpack("N*N*", $v));
        if (PHP_INT_SIZE >= 8) {
            if ($hi < 0) $hi += (1 << 32); // because php 5.2.2 to 5.2.5 is totally fucked up again
            if ($lo < 0) $lo += (1 << 32);
            // x64, int
            if ($hi <= 2147483647)
                return ($hi << 32) + $lo;
            // x64, bcmath
            if (function_exists("bcmul"))
                return bcadd($lo, bcmul($hi, "4294967296"));
            // x64, no-bcmath
            $C = 100000;
            $h = ((int)($hi / $C) << 32) + (int)($lo / $C);
            $l = (($hi % $C) << 32) + ($lo % $C);
            if ($l > $C) {
                $h += (int)($l / $C);
                $l = $l % $C;
            }
            if ($h == 0)
                return $l;
            return sprintf("%d%05d", $h, $l);
        }
        // x32, int
        if ($hi == 0) {
            if ($lo > 0)
                return $lo;
            return sprintf("%u", $lo);
        }
        $hi = sprintf("%u", $hi);
        $lo = sprintf("%u", $lo);
        // x32, bcmath
        if (function_exists("bcmul"))
            return bcadd($lo, bcmul($hi, "4294967296"));
        // x32, no-bcmath
        $hi = (float)$hi;
        $lo = (float)$lo;
        $q = floor($hi / 10000000.0);
        $r = $hi - $q * 10000000.0;
        $m = $lo + $r * 4967296.0;
        $mq = floor($m / 10000000.0);
        $l = $m - $mq * 10000000.0;
        $h = $q * 4294967296.0 + $r * 429.0 + $mq;
        $h = sprintf("%.0f", $h);
        $l = sprintf("%07.0f", $l);
        if ($h == "0")
            return sprintf("%.0f", (float)$l);
        return $h . $l;
    }

    /**
     * unpack 64-bit signed
     * @author 784855684@qq.com
     * @param $v
     * @return string
     */
    function sphUnpackI64($v)
    {
        list ($hi, $lo) = array_values(unpack("N*N*", $v));
        // x64
        if (PHP_INT_SIZE >= 8) {
            if ($hi < 0) $hi += (1 << 32); // because php 5.2.2 to 5.2.5 is totally fucked up again
            if ($lo < 0) $lo += (1 << 32);
            return ($hi << 32) + $lo;
        }
        // x32, int
        if ($hi == 0) {
            if ($lo > 0)
                return $lo;
            return sprintf("%u", $lo);
        } // x32, int
        elseif ($hi == -1) {
            if ($lo < 0)
                return $lo;
            return sprintf("%.0f", $lo - 4294967296.0);
        }
        $neg = "";
        $c = 0;
        if ($hi < 0) {
            $hi = ~$hi;
            $lo = ~$lo;
            $c = 1;
            $neg = "-";
        }
        $hi = sprintf("%u", $hi);
        $lo = sprintf("%u", $lo);
        // x32, bcmath
        if (function_exists("bcmul"))
            return $neg . bcadd(bcadd($lo, bcmul($hi, "4294967296")), $c);
        // x32, no-bcmath
        $hi = (float)$hi;
        $lo = (float)$lo;
        $q = floor($hi / 10000000.0);
        $r = $hi - $q * 10000000.0;
        $m = $lo + $r * 4967296.0;
        $mq = floor($m / 10000000.0);
        $l = $m - $mq * 10000000.0 + $c;
        $h = $q * 4294967296.0 + $r * 429.0 + $mq;
        if ($l == 10000000) {
            $l = 0;
            $h += 1;
        }
        $h = sprintf("%.0f", $h);
        $l = sprintf("%07.0f", $l);
        if ($h == "0")
            return $neg . sprintf("%.0f", (float)$l);
        return $neg . $h . $l;
    }

    function sphFixUint($value)
    {
        if (PHP_INT_SIZE >= 8) {
            // x64 route, workaround broken unpack() in 5.2.2+
            if ($value < 0) $value += (1 << 32);
            return $value;
        } else {
            // x32 route, workaround php signed/unsigned braindamage
            return sprintf("%u", $value);
        }
    }

    function sphSetBit($flag, $bit, $on)
    {
        if ($on) {
            $flag += (1 << $bit);
        } else {
            $reset = 255 ^ (1 << $bit);
            $flag = $flag & $reset;
        }
        return $flag;
    }


/// known searchd commands
    const SEARCHD_COMMAND_SEARCH = 0;
    const SEARCHD_COMMAND_EXCERPT = 1;
    const SEARCHD_COMMAND_UPDATE = 2;
    const SEARCHD_COMMAND_KEYWORDS = 3;
    const SEARCHD_COMMAND_PERSIST = 4;
    const SEARCHD_COMMAND_STATUS = 5;
    const SEARCHD_COMMAND_FLUSHATTRS = 7;
/// current client-side command implementation versions
    const VER_COMMAND_SEARCH = 0x11E;
    const VER_COMMAND_EXCERPT = 0x104;
    const VER_COMMAND_UPDATE = 0x103;
    const VER_COMMAND_KEYWORDS = 0x100;
    const VER_COMMAND_STATUS = 0x101;
    const VER_COMMAND_QUERY = 0x100;
    const VER_COMMAND_FLUSHATTRS = 0x100;
/// known searchd status codes
    const SEARCHD_OK = 0;
    const SEARCHD_ERROR = 1;
    const SEARCHD_RETRY = 2;
    const SEARCHD_WARNING = 3;
/// known match modes
    const SPH_MATCH_ALL = 0;
    const SPH_MATCH_ANY = 1;
    const SPH_MATCH_PHRASE = 2;
    const SPH_MATCH_BOOLEAN = 3;
    const SPH_MATCH_EXTENDED = 4;
    const SPH_MATCH_FULLSCAN = 5;
    const SPH_MATCH_EXTENDED2 = 6;    // extended engine V2 (TEMPORARY, WILL BE REMOVED)
/// known ranking modes (ext2 only)
    const SPH_RANK_PROXIMITY_BM25 = 0;    ///< default mode, phrase proximity major factor and BM25 minor one
    const SPH_RANK_BM25 = 1;    ///< statistical mode, BM25 ranking only (faster but worse quality)
    const SPH_RANK_NONE = 2;    ///< no ranking, all matches get a weight of 1
    const SPH_RANK_WORDCOUNT = 3;    ///< simple word-count weighting, rank is a weighted sum of per-field keyword occurence counts
    const SPH_RANK_PROXIMITY = 4;
    const SPH_RANK_MATCHANY = 5;
    const SPH_RANK_FIELDMASK = 6;
    const SPH_RANK_SPH04 = 7;
    const SPH_RANK_EXPR = 8;
    const SPH_RANK_TOTAL = 9;
/// known sort modes
    const SPH_SORT_RELEVANCE = 0;
    const SPH_SORT_ATTR_DESC = 1;
    const SPH_SORT_ATTR_ASC = 2;
    const SPH_SORT_TIME_SEGMENTS = 3;
    const SPH_SORT_EXTENDED = 4;
    const SPH_SORT_EXPR = 5;
/// known filter types
    const SPH_FILTER_VALUES = 0;
    const SPH_FILTER_RANGE = 1;
    const SPH_FILTER_FLOATRANGE = 2;
    const SPH_FILTER_STRING = 3;
/// known attribute types
    const SPH_ATTR_INTEGER = 1;
    const SPH_ATTR_TIMESTAMP = 2;
    const SPH_ATTR_ORDINAL = 3;
    const SPH_ATTR_BOOL = 4;
    const SPH_ATTR_FLOAT = 5;
    const SPH_ATTR_BIGINT = 6;
    const SPH_ATTR_STRING = 7;
    const SPH_ATTR_FACTORS = 1001;
    const SPH_ATTR_MULTI = 0x40000001;
    const SPH_ATTR_MULTI64 = 0x40000002;
/// known grouping functions
    const SPH_GROUPBY_DAY = 0;
    const SPH_GROUPBY_WEEK = 1;
    const SPH_GROUPBY_MONTH = 2;
    const SPH_GROUPBY_YEAR = 3;
    const SPH_GROUPBY_ATTR = 4;
    const SPH_GROUPBY_ATTRPAIR = 5;

}